import * as BABYLON from "babylonjs";
import type { GreasedLineMesh, Mesh, SolidParticleSystem } from 'babylonjs';
import Base from "../Base";
import Map from "../../map/Map";

export default class Ground {
  private base: Base;
  private ground: Mesh;
  private map: Map
  private points: SolidParticleSystem
  private bezierLineMeshs: BABYLON.GreasedLineBaseMesh[] | GreasedLineMesh[] | BABYLON.GreasedLineRibbonMesh[]
  private lineMeshs: BABYLON.GreasedLineBaseMesh[] | GreasedLineMesh[] | BABYLON.GreasedLineRibbonMesh[] | undefined

  constructor() {
    this.base = Base.getInstance();
    this.map = this.base.map

    this.ground = this.createGround()
    this.bezierLineMeshs = this.createBezierPath()
    this.lineMeshs = this.createStraightPath()

    this.createStations()
    this.points = this.createPoints()

    this.base.scene.addMesh(this.ground);
  }

  private createGround() {
    // 获取的宽度,长度和中心位置的信息
    const groundSize = this.map.getGroundSize()
    const center = this.map.getGroundCenter()
    const ground = BABYLON.MeshBuilder.CreateGround('ground', {
      width: groundSize.width,
      height: groundSize.height,
    })

    const groundMat = new BABYLON.StandardMaterial("groundMat");
    groundMat.emissiveColor = BABYLON.Color3.FromHexString("#73757e")
    groundMat.disableLighting = true;
    ground.material = groundMat;
    // set ground position
    ground.position = new BABYLON.Vector3(center.x, 0, center.z)
    return ground
  }

  private createStraightPath() {
    const paths = this.map.getStraightPath()

    const lineMeshs = []

    for (let path of paths) {
      const startPos = path.startPos.pos
      const endPos = path.endPos.pos
      const points = [new BABYLON.Vector3(startPos.x, 0, startPos.y), new BABYLON.Vector3(endPos.x, 0, endPos.y)];
      // 有可能会出现x或者y缺失的问题,这里先放着,后面遇到再来处理
      const center = new BABYLON.Vector3(((startPos.x ? startPos.x : 0) + endPos.x!) * 0.5, 0, (startPos.y! + endPos.y!) * 0.5)
      const direction = points[1].subtract(points[0]).normalize();

      const line = BABYLON.CreateGreasedLine(
        path.instanceName,
        {
          points: points,
          widthDistribution: BABYLON.GreasedLineMeshWidthDistribution.WIDTH_DISTRIBUTION_START,
        },
        {
          color: new BABYLON.Color3(0, 1, 1),
        },
      );
      // the arrow cap to the line instance to join it with the line
      const cap = BABYLON.GreasedLineTools.GetArrowCap(
        center,
        direction,
        0.4,
        4, 4
      );

      const lineMesh = BABYLON.CreateGreasedLine(
        path.instanceName,
        {
          points: cap.points,
          widths: cap.widths,
          widthDistribution: BABYLON.GreasedLineMeshWidthDistribution.WIDTH_DISTRIBUTION_START,
          instance: line
        },
      );
      line.position.y = 0.2
      lineMeshs.push(lineMesh)
    }

    return lineMeshs

  }

  private createBezierPath() {

    const paths = this.map.getBezierPath()

    const lineMeshs = []

    for (let path of paths) {

      const startPos = path.startPos.pos
      const endPos = path.endPos.pos
      const controlPos1 = path.controlPos1
      const controlPos2 = path.controlPos2

      const cubicBezierVectors = BABYLON.Curve3.CreateCubicBezier(
        new BABYLON.Vector3(startPos.x, 0, startPos.y),
        new BABYLON.Vector3(controlPos1?.x, 0, controlPos1?.y),
        new BABYLON.Vector3(controlPos2?.x, 0, controlPos2?.y),
        new BABYLON.Vector3(endPos.x, 0, endPos.y),
        6);


      const points = cubicBezierVectors.getPoints();
      const middlePointIdx = Math.floor(points.length / 2)
      const center = points[middlePointIdx]
      const direction = points[middlePointIdx + 1].subtract(points[middlePointIdx - 1]).normalize();

      // the arrow cap to the line instance to join it with the line
      const cap = BABYLON.GreasedLineTools.GetArrowCap(
        center,
        direction,
        0.4,
        5, 5
      );



      const line = BABYLON.CreateGreasedLine(
        path.instanceName,
        {
          points: cubicBezierVectors.getPoints(),
          widthDistribution: BABYLON.GreasedLineMeshWidthDistribution.WIDTH_DISTRIBUTION_START,

        },
        {
          color: new BABYLON.Color3(0, 1, 1),
        },
      );

      line.position.y = 0.2

      const lineMesh = BABYLON.CreateGreasedLine(
        path.instanceName,
        {
          points: cap.points,
          widths: cap.widths,
          widthDistribution: BABYLON.GreasedLineMeshWidthDistribution.WIDTH_DISTRIBUTION_START,
          instance: line
        },
      );

      lineMeshs.push(lineMesh)

    }

    return lineMeshs

  }

  public changePathColor(task_status: number, current_station: string, unfinished_path: string[]) {
    const lightPaths = this.handlePath(current_station, unfinished_path)

    if (task_status !== 2) { //恢复原来的颜色
      for (let line of this.bezierLineMeshs) {
        line.greasedLineMaterial!.color = new BABYLON.Color3(0, 1, 1)
      }

      if (this.lineMeshs) {
        for (let line of this.lineMeshs) {
          line.greasedLineMaterial!.color = new BABYLON.Color3(0, 1, 1)
        }
      }


    }


    if (task_status === 2) { //高亮路径的颜色
      for (let line of this.bezierLineMeshs) {
        if (lightPaths.includes(line.name)) {
          line.greasedLineMaterial!.color = new BABYLON.Color3(0, 1, 0)
        }
      }

      if (this.lineMeshs) {
        for (let line of this.lineMeshs) {
          if (lightPaths.includes(line.name)) {
            line.greasedLineMaterial!.color = new BABYLON.Color3(0, 1, 0)
          }
        }
      }

    }


  }

  private handlePath(current_station: string, unfinished_path: string[]) {
    if (current_station !== '') {
      unfinished_path.unshift(current_station)
    }
    const result = [];
    for (let i = 0; i < unfinished_path.length - 1; i++) {
      result.push(unfinished_path[i] + '-' + unfinished_path[i + 1]);
      result.push(unfinished_path[i + 1] + '-' + unfinished_path[i]);
    }
    return result
  }

  private createStations() {
    const stations = this.map.getStations()
    const groundMat = new BABYLON.StandardMaterial("groundMat");
    groundMat.emissiveColor = BABYLON.Color3.FromHexString("#f7ce46")
    groundMat.disableLighting = true;

    for (const station of stations) {
      const plane = BABYLON.MeshBuilder.CreateGround(station.instanceName, { height: 0.8, width: 0.8 });
      // 设置label
      plane.position = new BABYLON.Vector3(station.pos.x, 0.3, station.pos.y)
      const label = this.createStationLable(station.instanceName)
      plane.material = label;
      // plane.rotate(new BABYLON.Vector3(0, 1, 0), Math.PI)
    }

  }

  private createStationLable(stationName: string) {
    //Set font
    const font_size = 20;
    const font = "bold " + font_size + "px Arial";
    //Set height for dynamic texture
    const DTHeight = 2 * font_size; //or set as wished
    //Set text
    const text = Map.handleStationName(stationName);
    //Use a temporay dynamic texture to calculate the length of the text on the dynamic texture canvas
    const temp = new BABYLON.DynamicTexture("DynamicTexture", 32);
    const tmpctx = temp.getContext();
    tmpctx.font = font;
    const DTWidth = tmpctx.measureText(text).width + 8;
    //Create dynamic texture and write the text
    const dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", { width: DTWidth, height: DTHeight });
    const mat = new BABYLON.StandardMaterial("labelMaterial");
    mat.diffuseTexture = dynamicTexture;
    dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
    mat.emissiveColor = BABYLON.Color3.FromHexString("#f7ce46")
    mat.disableLighting = true;
    return mat
  }

  private createPoints() {
    const pointsInfo = this.map.getPoints()
    // custom position function
    const myPositionFunction = function (particle: Mesh, i: number) {
      particle.position = new BABYLON.Vector3(pointsInfo[i].x, 0.4, pointsInfo[i].y)
    };
    // Particle system creation : Immutable
    const points = new BABYLON.SolidParticleSystem('points', this.base.scene, { updatable: false });
    const point = BABYLON.MeshBuilder.CreateGround("point", {
      width: 0.03,
      height: 0.03
    })



    points.addShape(point, pointsInfo.length, { positionFunction: myPositionFunction });
    points.buildMesh();
    // 创建粒子之后,再讲物体颜色设置成亮色
    const pointMat = new BABYLON.StandardMaterial("groundMat");
    pointMat.emissiveColor = BABYLON.Color3.FromHexString("#ffffff")
    pointMat.disableLighting = true;
    points.mesh.material = pointMat;
    // points.mesh.isVisible = false
    return points
  }

  public togglePoints() {
    this.points.mesh.isVisible = !this.points.mesh.isVisible
  }


}


